package xj.graph2d.app.prop;

import static xj.app.BaseApplicationFrame.getResourceString;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Iterator;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSplitPane;
import javax.swing.JScrollPane;
import javax.swing.JTabbedPane;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;

import xj.graph2d.table.FeatureTable;
import xj.graph2d.table.FeatureTableEntry;
import xj.graph2d.table.FeatureTableListener;
import xj.meta.ModelElement;
import xj.model.uml.BasicModelElement;
import xj.model.uml.UMLClass;

abstract public class FeatureListField 
  extends ModelPropertyField {

  public FeatureListField() {
  }

  public FeatureListField(JFrame owner, 
			  String displayName, 
			  String dialogTitle) {
    this(owner, displayName, dialogTitle, true, false, null);
  }

  public FeatureListField(JFrame owner, 
			  String displayName, 
			  String dialogTitle, 
			  String labelKey) {
    this(owner, displayName, dialogTitle, true, false, labelKey);
  }

  public FeatureListField(JFrame owner, 
			  String displayName, 
			  String dialogTitle,
			  boolean showTextPanel, 
			  boolean isOperation) { 
    this(owner, displayName, dialogTitle, showTextPanel, isOperation, null);
  }

  public FeatureListField(JFrame owner, 
			  String displayName, 
			  String dialogTitle,
			  boolean showTextPanel, 
			  boolean isOperation,
			  String labelKey) {
    super(displayName);
    JButton editButton = makeEditButton(getResourceString("Edit.text"));

    featureDialog = new FeatureListDialog(owner, dialogTitle, this,
					  showTextPanel, isOperation, labelKey);
    editButton.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
	setActive(true);
	updateEditorValue();
	featureDialog.showDialog();
      }
    });
    setEditor(editButton);
  }

  public void setDialogSize(int w, int h) {
    if (featureDialog != null) {
      featureDialog.setSize(w, h);
    }
  }

  abstract FeatureTableEntry getPrototype();

  abstract FeatureTableEntry makeNewEntry();

  protected String getTextLabel() {
    return "feature";
  }

  public boolean isActive() { 
    return active;
  }
  
  public void setActive(boolean active) { 
    this.active= active;
  }

  protected boolean active = false; 

  protected FeatureListDialog featureDialog;

  protected FeatureListPanel featurePanel;

}

class FeatureListDialog extends JDialog {

  public FeatureListDialog(JFrame owner, 
			   String title, 
			   FeatureListField field) {
    this(owner, title, field, true, false, null);
  }

  public FeatureListDialog(JFrame owner, 
			   String title, 
			   FeatureListField field,
			   boolean showBottomPanel, 
			   boolean isOperation) {
    this(owner, title, field, showBottomPanel, isOperation, null);
  }

  public FeatureListDialog(JFrame owner, 
			   String title, 
			   FeatureListField field,
			   boolean showBottomPanel, 
			   boolean isOperation,
			   String labelKey) {
    super(owner, title, false); // non-modal //true);
    //setAlwaysOnTop(true);

    featurePanel = new FeatureListPanel(this, field, showBottomPanel,
					isOperation, labelKey);
    getContentPane().setLayout(new BorderLayout());
    getContentPane().add(featurePanel, BorderLayout.CENTER);

    setSize(500, 300); // default size
  }

  public void showDialog() {
    Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
    Dimension dialogSize = getSize();
    setLocation(screenSize.width / 2 - dialogSize.width / 2, 
		screenSize.height / 2 - dialogSize.height / 2);
    setVisible(true);
  }

  protected FeatureListPanel featurePanel;

}

class FeatureListPanel extends JPanel implements ActionListener {

  public FeatureListPanel(FeatureListDialog dialog, 
			  FeatureListField field) {
    this(dialog, field, true, false, null, null);
  }

  public FeatureListPanel(FeatureListDialog dialog, 
			  FeatureListField field,
			  boolean showBottomPanel, 
			  boolean isOperation) {
    this(dialog, field, showBottomPanel, isOperation, null, null);
  }

  public FeatureListPanel(FeatureListDialog dialog, 
			  FeatureListField field,
			  boolean showBottomPanel, 
			  boolean isOperation,
			  String labelKey) {
    this(dialog, field, showBottomPanel, isOperation, null, labelKey);
  }

  public FeatureListPanel(FeatureListDialog dialog, 
			  FeatureListField field,
			  boolean showBottomPanel, 
			  boolean isOperation, 
			  FeatureListPanel parent) {
    this(dialog, field, showBottomPanel, isOperation, parent, null);
  }

  public FeatureListPanel(FeatureListDialog dialog, 
			  FeatureListField field,
			  boolean showBottomPanel, 
			  boolean isOperation, 
			  FeatureListPanel parent, 
			  String labelKey) {
    this.dialog = dialog;
    this.field = field;
    this.showBottomPanel = showBottomPanel;
    this.parent = parent;

    setBorder(BorderFactory.createEmptyBorder(10, 10, 5, 10));
    setLayout(new BorderLayout());

    if (field != null) {
      field.featurePanel = this;
    }

    tablePanel = new JPanel();
    // tablePanel.setBackground(Color.white);
    tableTitle = new TitleComponent();
    tableTitle.setNameStyle(Font.ITALIC);
    tableTitle.setNameColor(new Color(0, 0, 128));
    tableTitle.setElementColor(new Color(0, 128, 0));
    tablePanel.setLayout(new BorderLayout());
    table = new FeatureTable(field.getPrototype());
    tablePanel.add(new JScrollPane(table), BorderLayout.CENTER);
    tablePanel.add(tableTitle, BorderLayout.NORTH);

    if (showBottomPanel) {
      table.addListSelectionListener(new ListSelectionListener() {
	public void valueChanged(ListSelectionEvent lse) {
	  // System.out.println("ListSelectionListener.valueChanged()");
	  updatePropertiesPanel();
	}
      });

      table.addFeatureTableListener(new FeatureTableListener() {
	public void editingStopped(ChangeEvent e) {
	  updatePropertiesPanel();
	}
      });

      constraintField = new ConstraintField();
      if (isOperation) {
	constraintField.setDefaultLanguageNames(languageNames);
	constraintField.setDefaultConstraintNames(constraintNames); 
      }
      constraintPanel = new ConstraintPanel(constraintField, true);

      commentField = new CommentField();
      commentPanel = new CommentPanel(commentField);

      stereotypeField = new StereotypeField();
      stereotypePanel = new StereotypePanel(stereotypeField);

      tagField = new TagField();
      tagPanel = new FeatureListPanel(null, tagField, false, false, this);

      tabbedPane = new JTabbedPane();
      if (isOperation) {
	parameterField = new ParameterField();
	parameterPanel = new FeatureListPanel(null, parameterField, false, false, this, "parameter");
	tabbedPane.add(getResourceString("Parameters.text"), parameterPanel);
      }
      String constraintsLabel =  getResourceString(isOperation ? "ConstraintsActions.text" : "Constraints.text");
      tabbedPane.add(constraintsLabel, constraintPanel);
      tabbedPane.add(getResourceString("Comments.text"), commentPanel);
      tabbedPane.add(getResourceString("Stereotypes.text"), stereotypePanel);
      tabbedPane.add(getResourceString("TaggedValues.text"), tagPanel);

      bottomPanel = new JPanel();
      bottomTitle = new TitleComponent(true);
      bottomTitle.setNameStyle(Font.ITALIC);
      bottomTitle.setNameColor(new Color(0, 0, 128));
      bottomTitle.setEntryStyle(Font.ITALIC);
      bottomTitle.setEntryColor(new Color(128, 128, 0));
      bottomTitle.setElementColor(new Color(0, 128, 0));

      bottomPanel.setLayout(new BorderLayout());
      bottomPanel.add(tabbedPane, BorderLayout.CENTER);
      bottomPanel.add(bottomTitle, BorderLayout.NORTH);
      setBottomTitle(field, 
		     getResourceString("PropertiesOf.text") + " " + field.getTextLabel(), null);

      JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, true, 
					    tablePanel, bottomPanel);
      splitPane.setDividerLocation(200);
      add(splitPane, BorderLayout.CENTER);
    } else {
      add(tablePanel, BorderLayout.CENTER);

      table.addListSelectionListener(new ListSelectionListener() {
	public void valueChanged(ListSelectionEvent lse) {
	  // System.out.println("ListSelectionListener.valueChanged()");
	  if (FeatureListPanel.this.parent != null) {
	    // finish();
	    if (FeatureListPanel.this.field != null) {
	      FeatureListPanel.this.field.updateModelValue();
	    }
	    FeatureListPanel.this.parent.writeToCurrentEntry();
	    FeatureListPanel.this.parent.table.repaint();
	    // FeatureListPanel.this.parent.updatePropertiesPanel();

	    // int c = table.getSelectedRow();
	    // table.tableChanged(new
	    // TableModelEvent(table.getModel(), c));
	  }
	}
      });

    }

    JPanel buttonPanel = new JPanel();
    buttonPanel.setBorder(BorderFactory.createEmptyBorder(5, 0, 0, 0));
    buttonPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
    String addText, deleteText; 
    if (labelKey == null) { 
      addText = getResourceString("Add.text");
      deleteText = getResourceString("Delete.text");
    } else { 
      addText = getResourceString("Add." + labelKey + ".text");
      deleteText = getResourceString("Delete." + labelKey + ".text");
    }
    addButton = new JButton(addText);
    addButton.addActionListener(this);
    buttonPanel.add(addButton);
    delButton = new JButton(deleteText);
    delButton.addActionListener(this);
    buttonPanel.add(delButton);
    // buttonPanel.add(Box.createHorizontalGlue());

    if (dialog != null) {
      cancelButton = new JButton(getResourceString("Cancel.text"));
      cancelButton.addActionListener(this);
      buttonPanel.add(cancelButton);
      okButton = new JButton(getResourceString("Done.text"));
      okButton.addActionListener(this);
      buttonPanel.add(okButton);
    } else {
      okButton = new JButton(getResourceString("OK.text"));
      okButton.addActionListener(this);
      buttonPanel.add(okButton);
    }
    add(buttonPanel, BorderLayout.SOUTH);
  }

  protected static String[] constraintNames = { "pre-condition", "post-condition", "action" };
  protected static String[] languageNames = { "informal", "OCL", "Java" };

  protected void updatePropertiesPanel() {
    int row = table.getSelectedRow();
    FeatureTableEntry newEntry = null;
    if (row >= 0 && row < entries.size()) {
      newEntry = entries.get(row);
    } else {
      newEntry = null;
    }
    if (curEntry != newEntry) {
      finishBottomPanel();
      writeToCurrentEntry();
    }
    curEntry = newEntry;
    readFromCurrentEntry();
  }

  /*
   * Update the editor based on the values of the model
   */
  protected void readFromCurrentEntry() {
    if (showBottomPanel) {
      String name = null;
      if (curEntry != null) {
	name = curEntry.getName();
	BasicModelElement model = curEntry.getModelElement();
	if (model != null) {
	  if (parameterField != null) {
	    parameterField.setModel(model);
	    parameterField.updateEditorValue();
	  }

	  if (constraintField != null) {
	    constraintField.setModel(model);
	    constraintField.updateEditorValue();
	  }

	  if (commentField != null) {
	    commentField.setModel(model);
	    commentField.updateEditorValue();
	  }

	  if (stereotypeField != null) {
	    stereotypeField.setModel(model);
	    stereotypeField.updateEditorValue();
	  }

	  if (tagField != null) {
	    tagField.setModel(model);
	    tagField.updateEditorValue();
	  }
	}
      }

      //String label = field.getTextLabel(name);
      //setBottomTitle("Properties of", label, null);
      setBottomTitle(field, "Properties of " + field.getTextLabel(), name);
    }
  }

  /*
   * Update the model based on the values of the editor
   */
  protected void writeToCurrentEntry() {
    if (showBottomPanel) {
      if (curEntry != null) {
	BasicModelElement model = curEntry.getModelElement();
	if (model != null) {
	  if (parameterField != null) {
	    parameterField.setModel(model);
	    parameterField.updateModelValue();
	  }

	  if (constraintField != null) {
	    constraintField.setModel(model);
	    constraintField.updateModelValue();
	  }

	  if (commentField != null) {
	    commentField.setModel(model);
	    commentField.updateModelValue();
	  }

	  if (stereotypeField != null) {
	    stereotypeField.setModel(model);
	    stereotypeField.updateModelValue();
	  }

	  if (tagField != null) {
	    tagField.setModel(model);
	    tagField.updateModelValue();
	  }
	}
      }
    }
  }

  public void setTableTitle(String prefix, String name, String element) {
    if (tablePanel != null && 
	tableTitle != null) {
      tableTitle.setTitle(prefix, name, element);
    }
  }

  public void setBottomTitle(FeatureListField field, String prefix, String name) {
    if (bottomPanel != null && 
	bottomTitle != null && 
	field != null) {
      if (name == null || name.length() == 0) {
	name = "<unnamed>";
      }
      String className;
      ModelElement model = field.getModel();
      if (model instanceof UMLClass) {
	UMLClass cls = (UMLClass) model;
	className = UMLClassModelPane.getClassName(cls);
      } else {
	className = "<unnamed>";
      }
      bottomTitle.setEntry(name);
      bottomTitle.setTitle(prefix, className, null);
    }
  }

  public void setEntries(java.util.List<FeatureTableEntry> entries) {
    this.entries = entries;
    table.setTableEntries(entries);
    curEntry = null;
    if (entries != null) {
      if (entries.size() > 0) {
	curEntry = entries.get(0);
	table.setRowSelectionInterval(0, 0);
      }
      readFromCurrentEntry();
    }
  }

  public java.util.List<FeatureTableEntry> getEntries() {
    return entries;
  }

  public void finish() {
    // System.out.println("FeatureListField.finish()");

    table.stopEditing();
    table.clearSelection();

    finishBottomPanel();

    if (field != null) {
      field.updateModelValue();
      field.updateShape();
    }

    if (parent != null) {
      parent.readFromCurrentEntry();
      parent.table.repaint();
    }
  }

  protected void finishBottomPanel() {
    if (showBottomPanel) {
      if (tagPanel != null) {
	tagPanel.finish();
      }
      if (parameterPanel != null) {
	parameterPanel.finish();
      }
      // to-do: update contraints, comments, stereotypes 
    }
  }

  public void actionPerformed(ActionEvent e) {
    Object source = e.getSource();
    if (source == okButton) {
      finish();
      if (dialog != null) {
	dialog.setVisible(false);
	field.setActive(false);
      }
    } else if (source == addButton) {
      // System.out.println("FeatureListPanel add");

      FeatureTableEntry newEntry = field.makeNewEntry();
      table.addEntry(newEntry);
    } else if (source == delButton) {
      if (entries != null) {
	Iterator<FeatureTableEntry> iter = entries.iterator();
	while (iter.hasNext()) {
	  FeatureTableEntry entry = iter.next();
	  if (entry.isDeleted()) {
	    iter.remove();
	  }
	}
	table.repaint();
	finish();
      }
    } else {
      // cancel
      if (dialog != null) {
	dialog.setVisible(false);
	field.setActive(false);
      }
    }
  }

  protected FeatureListDialog dialog;

  protected FeatureListField field;

  protected JButton okButton, cancelButton, addButton, delButton;

  protected FeatureTable table;

  protected JPanel tablePanel;
  protected TitleComponent tableTitle; 

  protected JPanel bottomPanel;
  protected TitleComponent bottomTitle; 

  protected JTabbedPane tabbedPane;

  protected boolean showBottomPanel;

  protected ConstraintPanel constraintPanel;

  protected ConstraintField constraintField;

  protected CommentPanel commentPanel;

  protected CommentField commentField;

  protected StereotypePanel stereotypePanel;

  protected StereotypeField stereotypeField;

  protected FeatureListPanel tagPanel;

  protected TagField tagField;

  protected FeatureListPanel parameterPanel;

  protected ParameterField parameterField;

  protected FeatureListPanel parent;

  protected java.util.List<FeatureTableEntry> entries;

  protected FeatureTableEntry curEntry = null;

}
